package com.lemon.cloud.command;

import com.google.common.collect.FluentIterable;
import com.lemon.cloud.exception.ExceptionHandler;
import com.lemon.cloud.interceptor.CommandInterceptor;
import com.lemon.cloud.representation.Response;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * @author lemon
 * @version 1.0
 * @description: TODO
 * @date Create by lemon on 2019-09-14 18:07
 */
@Slf4j
@Setter
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CommandInvocation {
    private Iterable<CommandInterceptor> preCommandInterceptors;

    private Iterable<CommandInterceptor> postCommandInterceptors;

    private CommandExecutor commandExecutor;

    @Autowired
    private ExceptionHandler exceptionHandler;

    private Response responseInstance;

    /**
     * @param command
     * @return com.lemon.cloud.representation.Response
     * @description
     * @author lemon
     * @date 2019-09-14 21:41
     */
    public <R extends Response, C extends Command> R invoke(C command) {
        R response = null;

        try {
            this.preInterceptor(command);
            response = (R) commandExecutor.execute(command);
        } catch (Exception e) {
            response = (R) this.responseInstance;
            this.exceptionHandler.handleException(command, response, e);
        } catch (Throwable t) {
            response = (R) this.responseInstance;
            this.exceptionHandler.handleThrowable(command, response, t);
        } finally {
            this.postInterceptor(command, response);
        }

        return response;
    }

    /**
     * @param command
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-14 21:41
     */
    private void preInterceptor(Command command) {
        try {
            for (CommandInterceptor preCommandInterceptor : FluentIterable.from(preCommandInterceptors).toSet()) {
                preCommandInterceptor.preCommand(command);
            }
        } catch (Exception e) {
            log.error("Failed to execute PreCommandInterceptor, error:" + e.getMessage(), e);
            throw new RuntimeException("Failed to execute PreCommandInterceptor", e);
        }
    }

    /**
     * @param command
     * @param response
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-14 21:41
     */
    private void postInterceptor(Command command, Response response) {
        try {
            for (CommandInterceptor postCommandInterceptor : FluentIterable.from(postCommandInterceptors).toSet()) {
                postCommandInterceptor.postCommand(command, response);
            }
        } catch (Exception e) {
            log.error("Failed to execute PostCommandInterceptor, error:" + e.getMessage(), e);
            throw new RuntimeException("Failed to execute PostCommandInterceptor", e);
        }
    }
}
